;***********************************************************************
; lock_no_IRQ.asm
;
; contains the system level-1 lock and unlock functions
; the lock system here also disables interrupts and then resets the interrupt
; enabled state to whatever it was before the lock was acquired
;
; Author: Aidan Goddard 11/7/14
;
; Uses portions of code from http://wiki.osdev.org/Spinlock
;
;***********************************************************************

; struct lock_table
; {
;	uint64_t lock_key;
;	uint64_t flags_state;
;	uint64_t reserved1;
;	uint64_t reserved2;
; }

section .text

global lock_no_IRQ
global unlock_no_IRQ

;/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
;/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
; spinlock entry
; rdi = pointer to the lock table (table should be 32 bytes in size)

; should be defined as:
; void lock_no_IRQ(struct lock_table *l)

lock_no_IRQ:
	; attempt to acquire the lock
	mov			eax, 0
	lock bts	[rdi], eax
	jnc			.acquired
	
.retest:
	; check if the lock is free before trying again
	pause
	test		DWORD [rdi], 1
	jnz			.retest
	
	; lock is open, attempt to acquire it
	mov			eax, 0
	lock bts	[rdi], eax
	jc			.retest
	
.acquired:
	; lock is acquired
	; save the flags state
	pushfq
	pop			rsi
	mov			[rdi + 8], rsi

	; disable interrupts
	cli

	; continue into critical region
	ret


;/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
;/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
; spinlock exit

; rdi = pointer to the lock table (table should be 32 bytes in size)

; should be defined as:
; void unlock_no_IRQ(struct lock_table *l)

unlock_no_IRQ:
	; release lock
	mov			[rdi], DWORD 0

	; restore flags
	mov			rsi, [rdi + 8]
	push		rsi
	popfq

	; done
	ret